//----------------------------------------------------------------------------------------------
//	Filename:	BasicSpace.cpp
//	Author:		Keith Bugeja
//	Date:		27/02/2010
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
#include <boost/format.hpp>
#include <boost/shared_ptr.hpp>

#include "Scene/Primitive.h"
#include "Scene/GeometricPrimitive.h"
#include "Space/BasicSpacePCS.h"
#include "Geometry/Intersection.h"

using namespace Illumina::Core;
//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Initialise(void) {  
	if (PrimitiveList.Size() == 1)
	{
		m_scene = PrimitiveList.Front();
		m_visiblePrimitiveList.PushBack(m_scene);
	}
	return true; 
}
//----------------------------------------------------------------------------------------------
void BasicSpacePCS::Shutdown(void)  
{ }
//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Build(void) { 
	return true; 
}
//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Update(void) { 
	return true; 
}

bool BasicSpacePCS::UpdateWorldTransforms(Transformation p_trans) 
{
	static float rot_angle = 0.0f; rot_angle+=0.1f;
	for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) ((GeometricPrimitive*)(m_obbs_it->second.primitive))->WorldTransform = p_trans;
	for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) ((GeometricPrimitive*)(m_grids_it->second.primitive))->WorldTransform = p_trans;
	for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) ((GeometricPrimitive*)(m_models_it->second.primitive))->WorldTransform = p_trans;
	return true;
}

bool BasicSpacePCS::AddOBB(std::string p_tag, IPrimitive * p_obbPrimitive) 
{
	if (m_obbs.find(p_tag) == m_obbs.end())
	{		
		m_obbs[p_tag] = PrimitiveEx(p_obbPrimitive, true);		
		m_visiblePrimitiveList.PushBack(p_obbPrimitive);
	}

	return true;
}

bool BasicSpacePCS::AddModel(std::string p_tag, IPrimitive * p_modelPrimitive, Matrix4x4 _translation, Matrix4x4 _rotation, Matrix4x4 _scale, Vector3 _local_rot_vector, Matrix4x4 _localTransform, Matrix4x4 _tr0, Matrix4x4 _tr1, Vector3 sf, Vector3 _local_translation)
{
	if (m_models.find(p_tag) == m_models.end())
	{		
		m_models[p_tag] = PrimitiveEx(p_modelPrimitive, true);
		m_models[p_tag].local_rotation = _rotation;
		m_models[p_tag].local_scale = _scale;
		m_models[p_tag].local_translation = _translation;
		m_models[p_tag].local_rotation_vector = _local_rot_vector;
		m_models[p_tag].tr_0 = _tr0;
		m_models[p_tag].tr_1 = _tr1;
		m_models[p_tag].local_transform = _localTransform;
		m_models[p_tag].local_translation_vector = _local_translation;
		m_models[p_tag].scale_factor = sf; 
		m_visiblePrimitiveList.PushBack(p_modelPrimitive);
		//std::cout << "Added new Model " << p_tag << " to visible set. Size=" << m_models.size() << " Visible List Size::" << m_visiblePrimitiveList.Size() << std::endl;
	}

	return true;
}

bool BasicSpacePCS::AddSegmentGrid(std::string p_tag, IPrimitive * p_gridPrimitive)
{
	if (m_grids.find(p_tag) == m_grids.end())
	{
		m_grids[p_tag] = PrimitiveEx(p_gridPrimitive, true);		
		m_visiblePrimitiveList.PushBack(p_gridPrimitive);
		//std::cout << "Added new Grid " << p_tag << " to visible set" << std::endl;
	}

	return true;
}

bool BasicSpacePCS::AddModel(std::string p_tag, IPrimitive * p_modelPrimitive)
{
	if (m_models.find(p_tag) == m_models.end())
	{
		m_models[p_tag] = PrimitiveEx(p_modelPrimitive, true);		
		m_visiblePrimitiveList.PushBack(p_modelPrimitive);
		std::cout << "Added new Model " << p_tag << " to visible set" << std::endl;
	}

	return true;
}

void BasicSpacePCS::ClearAllGrids()
{
	//Remove from visibility list then clear map	
	List<IPrimitive *> m_visiblePrimitiveListTemp;
	for (auto grids_it = m_grids.begin(); grids_it != m_grids.end(); grids_it++)
	{
		IPrimitive * toRemove = grids_it->second.primitive;		
		for (int i = 0; i<m_visiblePrimitiveList.Size(); i++) if (m_visiblePrimitiveList[i] != toRemove) m_visiblePrimitiveListTemp.PushBack(m_visiblePrimitiveList[i]);
		m_visiblePrimitiveList.Clear();
		for (int i = 0; i<m_visiblePrimitiveListTemp.Size(); i++) m_visiblePrimitiveList.PushBack(m_visiblePrimitiveListTemp[i]);
	}	
	m_grids.clear();
}

void BasicSpacePCS::ClearAllOBBs()
{
	//Remove from visibility list then clear map	
	List<IPrimitive *> m_visiblePrimitiveListTemp;
	for (auto obbs_it = m_obbs.begin(); obbs_it != m_obbs.end(); obbs_it++)
	{
		IPrimitive * toRemove = obbs_it->second.primitive;		
		for (int i = 0; i<m_visiblePrimitiveList.Size(); i++) if (m_visiblePrimitiveList[i] != toRemove) m_visiblePrimitiveListTemp.PushBack(m_visiblePrimitiveList[i]);
		m_visiblePrimitiveList.Clear();
		for (int i = 0; i<m_visiblePrimitiveListTemp.Size(); i++) m_visiblePrimitiveList.PushBack(m_visiblePrimitiveListTemp[i]);
	}	
	m_obbs.clear();
}

void BasicSpacePCS::ClearAllModels()
{
	//Remove from visibility list then clear map
	List<IPrimitive *> m_visiblePrimitiveListTemp;
	for (auto models_it = m_models.begin(); models_it != m_models.end(); models_it++)
	{
		IPrimitive * toRemove = models_it->second.primitive;		
		for (int i = 0; i<m_visiblePrimitiveList.Size(); i++) if (m_visiblePrimitiveList[i] != toRemove) m_visiblePrimitiveListTemp.PushBack(m_visiblePrimitiveList[i]);
		m_visiblePrimitiveList.Clear();
		for (int i = 0; i<m_visiblePrimitiveListTemp.Size(); i++) m_visiblePrimitiveList.PushBack(m_visiblePrimitiveListTemp[i]);
	}	
	m_models.clear();
}

bool BasicSpacePCS::SetVisibleOBB(std::string p_tag, bool p_visible) 
{
	if (m_obbs.find(p_tag) == m_obbs.end())
	{		
		return false;	
	}
	else
	{
		m_obbs[p_tag].visible = p_visible;
		if (p_visible==false)
		{			
			IPrimitive * toRemove = m_obbs[p_tag].primitive;
			List<IPrimitive *> m_visiblePrimitiveListTemp;
			for (int i = 0; i<m_visiblePrimitiveList.Size(); i++)
			{
				if (m_visiblePrimitiveList[i] != toRemove) m_visiblePrimitiveListTemp.PushBack(m_visiblePrimitiveList[i]);
			}
			m_visiblePrimitiveList.Clear();
			for (int i = 0; i<m_visiblePrimitiveListTemp.Size(); i++) m_visiblePrimitiveList.PushBack(m_visiblePrimitiveListTemp[i]);
		}
		else
		{
			m_visiblePrimitiveList.PushBack(m_obbs[p_tag].primitive);
		}
	}

	return true;
}

bool BasicSpacePCS::SetVisibleGrid(std::string p_tag, bool p_visible) 
{
	if (m_grids.find(p_tag) == m_grids.end())
	{		
		return false;
	}
	else
	{
		m_grids[p_tag].visible = p_visible;
		if (p_visible==false)
		{			
			IPrimitive * toRemove = m_grids[p_tag].primitive;
			List<IPrimitive *> m_visiblePrimitiveListTemp;
			for (int i = 0; i<m_visiblePrimitiveList.Size(); i++)
			{
				if (m_visiblePrimitiveList[i] != toRemove) m_visiblePrimitiveListTemp.PushBack(m_visiblePrimitiveList[i]);
			}
			m_visiblePrimitiveList.Clear();
			for (int i = 0; i<m_visiblePrimitiveListTemp.Size(); i++) m_visiblePrimitiveList.PushBack(m_visiblePrimitiveListTemp[i]);
		}
		else
		{
			m_visiblePrimitiveList.PushBack(m_grids[p_tag].primitive);
		}
	}

	return true;
}

bool BasicSpacePCS::SetVisibleModel(std::string p_tag, bool p_visible) 
{
	if (m_models.find(p_tag) == m_models.end())
	{		
		return false;
	}
	else
	{
		m_models[p_tag].visible = p_visible;
		if (p_visible==false)
		{			
			IPrimitive * toRemove = m_models[p_tag].primitive;
			List<IPrimitive *> m_visiblePrimitiveListTemp;
			for (int i = 0; i<m_visiblePrimitiveList.Size(); i++)
			{
				if (m_visiblePrimitiveList[i] != toRemove) m_visiblePrimitiveListTemp.PushBack(m_visiblePrimitiveList[i]);
			}
			m_visiblePrimitiveList.Clear();
			for (int i = 0; i<m_visiblePrimitiveListTemp.Size(); i++) m_visiblePrimitiveList.PushBack(m_visiblePrimitiveListTemp[i]);
		}
		else
		{
			m_visiblePrimitiveList.PushBack(m_models[p_tag].primitive);
		}
	}

	return true;
}

bool ReplaceAllGridsWithModels()
{

	return true;
}

bool ReplaceGridWithModel(std::string p_tag)
{
	
	return true;
}

bool BasicSpacePCS::SetVisibleAll(bool p_visibilityFlag) 
{
	m_visiblePrimitiveList.Clear();
	m_visiblePrimitiveList.PushBack(m_scene);
		
	if (p_visibilityFlag == true)
	{
		//show all
		for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) { m_models_it->second.visible = true; m_visiblePrimitiveList.PushBack(m_models_it->second.primitive); }
		for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) { m_obbs_it->second.visible = true; m_visiblePrimitiveList.PushBack(m_obbs_it->second.primitive); }
		for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) { m_grids_it->second.visible = true; m_visiblePrimitiveList.PushBack(m_grids_it->second.primitive); }
	}
	else
	{
		//hide all
		for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) m_obbs_it->second.visible = false;
		for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) m_models_it->second.visible = false;
		for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) m_grids_it->second.visible = false;
	}

	return true;
}
				
bool BasicSpacePCS::SetVisibleAllOBBs(bool p_visibilityFlag) {	
	m_visiblePrimitiveList.Clear();		
	if (p_visibilityFlag==false)
	{
		//Hide All	
		for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) m_obbs_it->second.visible = false;		
	}
	else 
	{
		//Show All OBBs		
		for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) 
		{
			m_obbs_it->second.visible = true;
			m_visiblePrimitiveList.PushBack(m_obbs_it->second.primitive);
		}

	}

	m_visiblePrimitiveList.PushBack(m_scene);		

	for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) 	
		if (m_models_it->second.visible) m_visiblePrimitiveList.PushBack(m_models_it->second.primitive);

	for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) 	
		if (m_grids_it->second.visible) m_visiblePrimitiveList.PushBack(m_grids_it->second.primitive);
	
	return true;
}

bool BasicSpacePCS::SetVisibleAllModels(bool p_visibilityFlag)
{
	m_visiblePrimitiveList.Clear();		
	if (p_visibilityFlag==false)
	{
		//Hide All		
		for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) m_models_it->second.visible = false;		
	}
	else 
	{
		//Show All Models		
		for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) 
		{
			m_models_it->second.visible = true;
			m_visiblePrimitiveList.PushBack(m_models_it->second.primitive);
		}
	}

	m_visiblePrimitiveList.PushBack(m_scene);		

	for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) 	
		if (m_obbs_it->second.visible) m_visiblePrimitiveList.PushBack(m_obbs_it->second.primitive);

	for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) 	
		if (m_grids_it->second.visible) m_visiblePrimitiveList.PushBack(m_grids_it->second.primitive);
	
	return true;
}

bool BasicSpacePCS::SetVisibleAllGrids(bool p_visibilityFlag)
{
	m_visiblePrimitiveList.Clear();		
	if (p_visibilityFlag==false)
	{
		//Hide All		
		for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) m_grids_it->second.visible = false;		
	}
	else 
	{
		//Show All Models		
		for (auto m_grids_it = m_grids.begin(); m_grids_it != m_grids.end(); m_grids_it++) 
		{
			m_grids_it->second.visible = true;
			m_visiblePrimitiveList.PushBack(m_grids_it->second.primitive);
		}
	}

	m_visiblePrimitiveList.PushBack(m_scene);

	for (auto m_obbs_it = m_obbs.begin(); m_obbs_it != m_obbs.end(); m_obbs_it++) 	
		if (m_obbs_it->second.visible) m_visiblePrimitiveList.PushBack(m_obbs_it->second.primitive);

	for (auto m_models_it = m_models.begin(); m_models_it != m_models.end(); m_models_it++) 	
		if (m_models_it->second.visible) m_visiblePrimitiveList.PushBack(m_models_it->second.primitive);
	
	return true;
}

bool BasicSpacePCS::SetVisiblePCS(bool p_visibilityFlag)
{
	if (p_visibilityFlag == true)
	{

	}
	else
	{

	}

	return true;
}

//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Intersects(const Ray &p_ray, Intersection &p_intersection) const
{
	bool bIntersect = false;
	Ray ray(p_ray);

	for (int index = 0, count = (int)m_visiblePrimitiveList.Size(); index < count; index++)
	{
		if (m_visiblePrimitiveList[index]->Intersects(ray, p_intersection))
		{
			bIntersect = true;
			ray.Max = Maths::Min(ray.Max, p_intersection.Surface.Distance);
		}
	}

	return bIntersect;
}
//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Intersects(const Ray &p_ray, Intersection &p_intersection, IPrimitive *p_pExclude) const
{
	bool bIntersect = false;
	Ray ray(p_ray);

	for (int index = 0, count = (int)m_visiblePrimitiveList.Size(); index < count; index++)
	{
		if (m_visiblePrimitiveList[index] != p_pExclude && 
			m_visiblePrimitiveList[index]->Intersects(ray, p_intersection))
		{
			bIntersect = true;
			ray.Max = Maths::Min(ray.Max, p_intersection.Surface.Distance);
		}
	}

	return bIntersect;
}
//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Intersects(const Ray &p_ray) const
{
	for (int primitiveIdx = 0, count = (int)m_visiblePrimitiveList.Size(); primitiveIdx < count; primitiveIdx++)
	{
		if (m_visiblePrimitiveList[primitiveIdx]->Intersects(p_ray))
			return true;
	}

	return false;
}
//----------------------------------------------------------------------------------------------
bool BasicSpacePCS::Intersects(const Ray &p_ray, IPrimitive *p_pExclude) const
{
	for (int primitiveIdx = 0, count = (int)m_visiblePrimitiveList.Size(); primitiveIdx < count; primitiveIdx++)
	{
		if (m_visiblePrimitiveList[primitiveIdx] != p_pExclude && 
			m_visiblePrimitiveList[primitiveIdx]->Intersects(p_ray))
			return true;
	}

	return false;
}
//----------------------------------------------------------------------------------------------
